package com.ruoyi.project.wenyanwen.log.task.impl;

import com.github.pagehelper.PageHelper;
import com.ruoyi.common.utils.JsonUtil;
import com.ruoyi.project.wenyanwen.data.domain.UserData;
import com.ruoyi.project.wenyanwen.data.domain.UserFeature;
import com.ruoyi.project.wenyanwen.data.service.IUserDataService;
import com.ruoyi.project.wenyanwen.log.domain.TrainLog;
import com.ruoyi.project.wenyanwen.log.service.ITrainLogService;
import com.ruoyi.project.wenyanwen.log.task.TrainTask;
import com.ruoyi.project.wenyanwen.score.domain.UserScore;
import com.ruoyi.project.wenyanwen.score.service.IUserScoreService;
import com.ruoyi.recommend.common.DataConverter;
import com.ruoyi.recommend.common.DataSet;
import com.ruoyi.recommend.stars.model.ItemNeighborFactorization;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author jackyshi
 */
@Component("train")
public class TrainTaskImpl implements TrainTask {

    private static final Logger log = LoggerFactory.getLogger(TrainTaskImpl.class);

    @Autowired
    ITrainLogService trainLogService;

    @Autowired
    IUserDataService userDataService;

    @Autowired
    @Qualifier("commonRedisTemplate")
    RedisTemplate<String, String> redisTemplate;

    @Autowired
    IUserScoreService userScoreService;

    @Override
    public void run() {

        TrainLog trainLog = trainLogService.selectTrainTask();
        //if (trainLog != null) {
        TrainContext ctx = new TrainContext();
        start(trainLog, ctx);
        //}
    }

    private void start(TrainLog trainLog, TrainContext ctx) {
        prepare(ctx);

        //用户行为数据打分
        //startScoreTask(ctx);

        //训练数据转换
        startCovertTrainData(ctx);

        //开始训练
        startTrain(ctx);

        //保存模型
        saveModel(ctx);

        finish(ctx);

    }

    private void saveModel(TrainContext ctx) {
    }

    private void startTrain(TrainContext ctx) {

        try {

            log.info("start to slope one model training, data path[{}]", ctx.getTraingDataPath());
            DataSet traindata = new DataSet.MemDataSet(ctx.getTraingDataPath());
            ItemNeighborFactorization model = new ItemNeighborFactorization();
            model.loadData(traindata);
            model.train();
            log.info("save model to[{}]", ctx.getModelPath());
            model.saveModel(ctx.getModelPath());
            log.info("finish~");
        } catch (Exception ie) {
            log.error(ie.getMessage(), ie);
        }
    }

    private void startCovertTrainData(TrainContext ctx) {

        try {
            DataConverter.setSplit(",");
            DataConverter.serilizeRatingFile(ctx.getTraingDataPath());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private void startScoreDataExport(TrainContext ctx) {
        BufferedWriter writer = null;
        try {
            int page = 1;
            int size = 1000;
            UserScore query = new UserScore();
            while (true) {
                PageHelper.startPage(page, 1000, false);
                List<UserScore> scores = userScoreService.selectUserScoreList(query);
                if (scores.isEmpty()) {
                    break;
                }
                writer = new BufferedWriter(new FileWriter(ctx.getTraingDataPath()));
                for (UserScore score : scores) {
                    writer.write(String.format("%s,%s,%s", score.getUid(), score.getItemId(), score.getScore()));
                    writer.newLine();
                }
            }
        } catch (IOException ie) {
            IOUtils.closeQuietly(writer);
        }
    }

    private void finish(TrainContext ctx) {
    }

    private void prepare(TrainContext ctx) {

        userScoreService.truncateUserScore();

    }

    private void startScoreTask(TrainContext ctx) {
        int pageNum = 1;
        int size = 1000;
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(ctx.getTraingDataPath()));
            while (true) {
                UserData query = new UserData();
                PageHelper.startPage(pageNum, 1000, false);
                List<UserData> userDatas = userDataService.selectUserDataList(query);
                if (userDatas.isEmpty()) {
                    break;
                }
                pageNum += 1;
                for (UserData userData : userDatas) {
                    UserFeature userFeature = JsonUtil.toObject(userData.getData(), UserFeature.class);
                    List<UserScore> scores = getScores(userData.getUuid(), userFeature, ctx);
                    for (UserScore score : scores) {
                        log.info("uid[{}], itemId[{}], score[{}]", score.getUid(), score.getItemId(), score.getScore());
                        writer.write(String.format("%s,%s,%s", score.getUid(), score.getItemId(), score.getScore()));
                        writer.newLine();
                    }
                }
            }
        } catch (IOException ie) {
            log.error(ie.getMessage(), ie);
        } finally {
            IOUtils.closeQuietly(writer);
        }

    }

    private List<UserScore> getScores(String uuid, UserFeature userFeature, TrainContext ctx) {
        //查看 2 +  (n-1) * 0.1
        //背诵 3 + （n-1） * 0.2
        int uid = ctx.getUidMapperId(uuid);
        List<UserScore> scores = new ArrayList<>();
        Map<Integer, Integer> scoreMap = new HashMap<>();
        calScore(userFeature.getWatchCount(), scoreMap, 30, 5, ctx);
        calScore(userFeature.getReciteCount(), scoreMap, 60, 10, ctx);

        for (Map.Entry<Integer, Integer> entry : scoreMap.entrySet()) {
            UserScore userScore = new UserScore();
            userScore.setUid((long) uid);
            userScore.setItemId((long) entry.getKey());
            int score = entry.getValue();
            score = score > 100 ? 100 : score;
            userScore.setScore((long) score);
            scores.add(userScore);
        }

        return scores;
    }

    private void calScore(Map<Integer, Long> data, Map<Integer, Integer> scoreMap, int baseScore, int perScore, TrainContext ctx) {
        for (Map.Entry<Integer, Long> entry : data.entrySet()) {
            Integer itemId = ctx.getIidMapperId(entry.getKey());
            int score = 0;
            if (scoreMap.containsKey(itemId)) {
                score = scoreMap.get(itemId);
            }
            int count = entry.getValue().intValue();
            score = baseScore + (count - 1) * perScore;
            scoreMap.put(itemId, score);
        }
    }

}
